package com.ndp.fb.rdb.mongo.impl;

import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import com.mongodb.*;
import com.ndp.fb.constant.MongoConstants;
import com.ndp.fb.mongo.api.AdMarketTargetStatsMongoService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Created by jacky.cheng on 2015/6/18.
 */
@Service
public class AdMarketTargetStatsMongoServiceImpl implements AdMarketTargetStatsMongoService {
    private final Logger logger = LoggerFactory.getLogger(getClass());
    @Autowired
    private DB mongoDBClient;

    private final static String[] AD_MARKET_TARGET_LIST_BUSINESS_FIELD = new String[]{"id", "productId", "targetCountry", "feedWeId", "dayBudget", "status", "targetRoi", "name", "createTime"};

    private final static String[] AD_MARKET_TARGET_LIST_STATS_FIELD = new String[]{"salesAmount", "spent", "roi", "gaTransactionRevenue", "gaRoi"};

    @Override
    public void saveOrUpdateByAdMarketTargetId(JSONObject jsonObject) {
        long startTime = System.currentTimeMillis();
        try {
            // 更新条件
            DBObject query = new BasicDBObject();
            query.put("adMarketTargetId", jsonObject.get("adMarketTargetId"));

            // 更新内容
            jsonObject.put("dataUpdateTime", new Date());
            DBObject update = new BasicDBObject();
            update.put("$set", JSON.parseObject(jsonObject.toJSONString(), Feature.DisableCircularReferenceDetect));

            DBCollection dbCollection = mongoDBClient.getCollection(MongoConstants.TABLE_NAME_ADMARKETTARGET_STATS);
            dbCollection.update(query, update, true, true);
        } catch (Exception e) {
            logger.error("更新营销目标统计数据出错，原始数据：{}", JSON.toJSONString(jsonObject), e);
        } finally {
            logger.debug("更新营销目标统计数据耗时{}毫秒，原始数据：{}", System.currentTimeMillis() - startTime, JSON.toJSONString(jsonObject));
        }
    }

    @Override
    public JSONObject getAdMarketListWithStatsData(JSONObject param, String ticket) {
        // 过滤，根据用户进行过滤
        BasicDBObject matchChildObject1 = new BasicDBObject("userId", ticket);

        //  lookup，拼接统计数据
        BasicDBObject lookupChildObject1 = new BasicDBObject()
                .append("from", "admarkettargetstats")
                .append("localField", "id")
                .append("foreignField", "adMarketTargetId")
                .append("as", "stats");

        // unwind，将lookup查询到的统计数据拆分开，方便后面处理
        BasicDBObject unwindChildObject1 = new BasicDBObject()
                .append("path", "$stats")
                .append("preserveNullAndEmptyArrays", true);

        BasicDBObject projectChildObject1 = new BasicDBObject()
                .append("_id", 0);
        for (int i = 0; i < AD_MARKET_TARGET_LIST_BUSINESS_FIELD.length; i++) {
            projectChildObject1.append(AD_MARKET_TARGET_LIST_BUSINESS_FIELD[i], 1);
        }

        // 排序
        BasicDBObject sortChildObject1 = new BasicDBObject()
                .append(param.getString("sortField"), param.getInteger("sortType"))
                .append("id", 1);

        // 分组，计算统计行
        BasicDBObject groupChildObject1 = new BasicDBObject()
                .append("_id", null)
                .append("data", new BasicDBObject("$push", "$$CURRENT"))
                .append("count", new BasicDBObject("$sum", 1));

        Integer cPage = param.getInteger("cPage");
        Integer pSize = param.getInteger("pSize");
        int startNum = (cPage - 1) * pSize;
        BasicDBObject projectChildObject2 = new BasicDBObject()
                .append("data", new BasicDBObject("$slice", new Object[]{"$data", startNum, pSize}))
                .append("summary.count", "$count");

        for (int i = 0; i < AD_MARKET_TARGET_LIST_STATS_FIELD.length; i++) {
            String key = AD_MARKET_TARGET_LIST_STATS_FIELD[i];
            String keyForYesterday = "yesterday" + key.replaceFirst(key.substring(0, 1), key.substring(0, 1).toUpperCase());
            String keyForToday = "today" + key.replaceFirst(key.substring(0, 1), key.substring(0, 1).toUpperCase());
            switch (key) {
                case "roi": // roi要进行计算，特殊处理
                    projectChildObject1.append(keyForYesterday, new BasicDBObject("$ifNull", new Object[]{
                            new BasicDBObject("$cond", new Object[]{
                                    new BasicDBObject("$eq", new Object[]{0, "$stats.yesterdaySpent"}),
                                    0,
                                    new BasicDBObject("$divide", new Object[]{"$stats.yesterdaySalesAmount", "$stats.yesterdaySpent"})
                            }),
                            0
                    }));
                    projectChildObject1.append(keyForToday, new BasicDBObject("$ifNull", new Object[]{
                            new BasicDBObject("$cond", new Object[]{
                                    new BasicDBObject("$eq", new Object[]{0, "$stats.todaySpent"}),
                                    0,
                                    new BasicDBObject("$divide", new Object[]{"$stats.todaySalesAmount", "$stats.todaySpent"})
                            }),
                            0
                    }));
                    projectChildObject1.append(key, new BasicDBObject("$ifNull", new Object[]{
                            new BasicDBObject("$cond", new Object[]{
                                    new BasicDBObject("$eq", new Object[]{0, "$stats.spent"}),
                                    0,
                                    new BasicDBObject("$divide", new Object[]{"$stats.salesAmount", "$stats.spent"})
                            }),
                            0
                    }));

                    projectChildObject2.append("summary." + keyForYesterday, new BasicDBObject("$cond", new Object[]{
                            new BasicDBObject("$eq", new Object[]{0, "$yesterdaySpent"}),
                            0,
                            new BasicDBObject("$divide", new Object[]{"$yesterdaySalesAmount", "$yesterdaySpent"})
                    }));
                    projectChildObject2.append("summary." + keyForToday, new BasicDBObject("$cond", new Object[]{
                            new BasicDBObject("$eq", new Object[]{0, "$todaySpent"}),
                            0,
                            new BasicDBObject("$divide", new Object[]{"$todaySalesAmount", "$todaySpent"})
                    }));
                    projectChildObject2.append("summary." + key, new BasicDBObject("$cond", new Object[]{
                            new BasicDBObject("$eq", new Object[]{0, "$spent"}),
                            0,
                            new BasicDBObject("$divide", new Object[]{"$salesAmount", "$spent"})
                    }));
                    break;
                case "gaRoi": // roi要进行计算，特殊处理
                    projectChildObject1.append(keyForYesterday, new BasicDBObject("$ifNull", new Object[]{
                            new BasicDBObject("$cond", new Object[]{
                                    new BasicDBObject("$eq", new Object[]{0, "$stats.yesterdaySpent"}),
                                    0,
                                    new BasicDBObject("$divide", new Object[]{"$stats.yesterdayGaTransactionRevenue", "$stats.yesterdaySpent"})
                            }),
                            0
                    }));
                    projectChildObject1.append(keyForToday, new BasicDBObject("$ifNull", new Object[]{
                            new BasicDBObject("$cond", new Object[]{
                                    new BasicDBObject("$eq", new Object[]{0, "$stats.todaySpent"}),
                                    0,
                                    new BasicDBObject("$divide", new Object[]{"$stats.todayGaTransactionRevenue", "$stats.todaySpent"})
                            }),
                            0
                    }));
                    projectChildObject1.append(key, new BasicDBObject("$ifNull", new Object[]{
                            new BasicDBObject("$cond", new Object[]{
                                    new BasicDBObject("$eq", new Object[]{0, "$stats.spent"}),
                                    0,
                                    new BasicDBObject("$divide", new Object[]{"$stats.gaTransactionRevenue", "$stats.spent"})
                            }),
                            0
                    }));

                    projectChildObject2.append("summary." + keyForYesterday, new BasicDBObject("$cond", new Object[]{
                            new BasicDBObject("$eq", new Object[]{0, "$yesterdaySpent"}),
                            0,
                            new BasicDBObject("$divide", new Object[]{"$yesterdayGaTransactionRevenue", "$yesterdaySpent"})
                    }));
                    projectChildObject2.append("summary." + keyForToday, new BasicDBObject("$cond", new Object[]{
                            new BasicDBObject("$eq", new Object[]{0, "$todaySpent"}),
                            0,
                            new BasicDBObject("$divide", new Object[]{"$todayGaTransactionRevenue", "$todaySpent"})
                    }));
                    projectChildObject2.append("summary." + key, new BasicDBObject("$cond", new Object[]{
                            new BasicDBObject("$eq", new Object[]{0, "$spent"}),
                            0,
                            new BasicDBObject("$divide", new Object[]{"$gaTransactionRevenue", "$spent"})
                    }));
                    break;
                default:
                    projectChildObject1.append(keyForYesterday, new BasicDBObject("$ifNull", new Object[]{"$stats." + keyForYesterday, 0}));
                    projectChildObject1.append(keyForToday, new BasicDBObject("$ifNull", new Object[]{"$stats." + keyForToday, 0}));
                    projectChildObject1.append(key, new BasicDBObject("$ifNull", new Object[]{"$stats." + key, 0}));
                    groupChildObject1.append(keyForYesterday, new BasicDBObject("$sum", "$" + keyForYesterday));
                    groupChildObject1.append(keyForToday, new BasicDBObject("$sum", "$" + keyForToday));
                    groupChildObject1.append(key, new BasicDBObject("$sum", "$" + key));
                    projectChildObject2.append("summary." + keyForYesterday, "$" + keyForYesterday);
                    projectChildObject2.append("summary." + keyForToday, "$" + keyForToday);
                    projectChildObject2.append("summary." + key, "$" + key);
                    break;
            }
        }

        List<DBObject> aggregateObjects = new ArrayList<DBObject>();
        aggregateObjects.add(new BasicDBObject("$match", matchChildObject1));
        aggregateObjects.add(new BasicDBObject("$lookup", lookupChildObject1));
        aggregateObjects.add(new BasicDBObject("$unwind", unwindChildObject1));
        aggregateObjects.add(new BasicDBObject("$project", projectChildObject1));
        aggregateObjects.add(new BasicDBObject("$sort", sortChildObject1));
        aggregateObjects.add(new BasicDBObject("$group", groupChildObject1));
        aggregateObjects.add(new BasicDBObject("$project", projectChildObject2));
        logger.info("Mongo查询语句:" + com.mongodb.util.JSON.serialize(aggregateObjects));
        long start = System.currentTimeMillis();
        AggregationOptions aggregationOptions = AggregationOptions.builder().allowDiskUse(true).build();
        Cursor cursor = mongoDBClient.getCollection(MongoConstants.TABLE_NAME_ADMARKETTARGET).aggregate(aggregateObjects, aggregationOptions);
        logger.info("Mongo聚合查询花费时间：" + (System.currentTimeMillis() - start) + "毫秒");
        JSONObject result = new JSONObject();
        try {
            if (cursor.hasNext()) {
                DBObject dbObject = cursor.next();
                result = JSON.parseObject(JSON.toJSONString(dbObject));
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        } finally {
            cursor.close();
        }
        return result;
    }
}
